<?php

namespace app\admin\service;

use app\api\service\SaoMaFuService;
use app\model\Money;
use app\model\OfficialAccounts;
use app\model\Admin;
use app\model\WatchPayRecord;
use app\model\PayRecord;
use app\model\Records;
use app\model\Videos;
use app\model\User;
use app\model\VideoDetails;
use app\model\Link;
use Carbon\Carbon;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use app\model\ResponsibilityPerson;
use app\model\Company;
use app\common\Common;
use app\model\RefundRecord;
use app\common\Error;
use app\model\Applet;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\Exception;
use think\facade\Log;
use think\facade\Db;
use think\helper\Arr;

/**
 * 记录服务类
 */
class RecordService
{
    private $officialAccountsModel;
    private $adminModel;
    private $payRecordModel;
    private $watchPayRecordModel;
    private $recordsModel;
    private $videosModel;
    private $userModel;
    private $detailModel;
    private $linkModel;
    private $responsibilityPersonModel;
    private $companyModel;
    private $refundRecordModel;
    private $appletModel;

    public function __construct(OfficialAccounts $officialAccountsModel, Admin $adminModel, PayRecord $payRecordModel, WatchPayRecord $watchPayRecordModel, Records $recordsModel, Videos $videosModel, User $userModel, VideoDetails $detailModel, Link $linkModel, ResponsibilityPerson $responsibilityPersonModel, Company $companyModel, RefundRecord $refundRecordModel, Applet $appletModel)
    {
        $this->officialAccountsModel = $officialAccountsModel;
        $this->adminModel = $adminModel;
        $this->payRecordModel = $payRecordModel;
        $this->watchPayRecordModel = $watchPayRecordModel;
        $this->recordsModel = $recordsModel;
        $this->videosModel = $videosModel;
        $this->userModel = $userModel;
        $this->detailModel = $detailModel;
        $this->linkModel = $linkModel;
        $this->responsibilityPersonModel = $responsibilityPersonModel;
        $this->companyModel = $companyModel;
        $this->refundRecordModel = $refundRecordModel;
        $this->appletModel = $appletModel;
    }

    // 获取充值记录列表数据
    public function getRechargeList(array $params)
    {
        $page = $params['page'] ?? 1;
        $limit = $params['limit'] ?? 10;

        $where = [];
        if (!empty($params['uid'])) {
            $where[] = ['uid', '=', $params['uid']];
        }

        if (isset($params['status']) && is_numeric($params['status'])) {
            $where[] = ['status', '=', $params['status']];
        }

        if (!empty($params['video_id'])) {
            $where[] = ['video_id', '=', $params['video_id']];
        }

        if (!empty($params['type'])) {
            $where[] = ['type', '=', $params['type']];
        }

        if (!empty($params['link_id'])) {
            $where[] = ['link_id', '=', $params['link_id']];
        }

        if (!empty($params['responsibility_person_id'])) {
            $linkData = $this->linkModel->getDataByWhere(['responsibility_person_id' => $params['responsibility_person_id']], 'id');
            $linkIds = array_column($linkData, 'id');
            $where[] = ['link_id', 'in', $linkIds];
        }

        if (!empty($params['order_no'])) {
            $where[] = ['order_no', 'like',  $params['order_no'] . '%'];
        }

        if (!empty($params['start_date'])) {
            $startDate = strtotime($params['start_date'] . ' 00:00:00');
            $where[] = ['pay_time', '>=', $startDate];
        }

        if (!empty($params['end_date'])) {
            $endDate = strtotime($params['end_date'] . ' 23:59:59');
            $where[] = ['pay_time', '<=', $endDate];
        }

        if (!empty($params['source'])) {
            $where[] = ['source', '=', $params['source']];
        }

        // 支付商户
        if (!empty($params['pay_merchant'])) {
            $where[] = ['pay_merchant', '=', $params['pay_merchant']];
        }

        // 微信订单号
        if (!empty($params['wechat_order_no'])) {
            $where[] = ['wechat_order_no', 'like', $params['wechat_order_no'] . '%'];
        }

        // 平台订单号
        if (!empty($params['platform_order_no'])) {
            $where[] = ['platform_order_no', 'like',  $params['platform_order_no'] . '%'];
        }

        // 公司搜索
        if (!empty($params['company_id'])) {
            $responsibilityPerson = $this->responsibilityPersonModel->getDataByWhere(['company_id' => $params['company_id']], 'id, is_distributor');

            $responsibilityPersonIds = array_column($responsibilityPerson, 'id');

            if (!empty($responsibilityPerson[0]['is_distributor'])) {
                $responsibilityPerson = $this->responsibilityPersonModel->getDataByWhere(['pid' => $responsibilityPersonIds], 'id');
                $responsibilityPersonIds = array_column($responsibilityPerson, 'id');
            }

            $linkData1 = $this->linkModel->getDataByWhere(['responsibility_person_id' => $responsibilityPersonIds], 'id');
            $linkIds1 = array_column($linkData1, 'id');
            $where[] = ['link_id', 'in', $linkIds1];
        }

        $data = $this->payRecordModel->getPagingDataByWhere($where, $page, $limit, 'id, type, uid, order_no, total_fee, created_at, pay_time, status, link_id, activation_time, video_id, number, pay_merchant, device_type, source, refund_status');

        if (!empty($data['data'])) {

            $videoIds = array_unique(array_column($data['data'], 'video_id'));

            if (!empty($videoIds)) {
                $videoData = $this->videosModel->getDataByIds($videoIds, 'id, name');
                $videoResult = array_column($videoData, 'name', 'id');
            }

            $userIds = array_unique(array_column($data['data'], 'uid'));
            // $userData = $this->userModel->getBatchDataByWhere(['id' => $userIds], 'id, official_account_id, applet_id, created_at, official_account_created_at');
            $userData = Db::name('user')->alias('a')->leftjoin('applet b', 'b.id = a.applet_id')->leftjoin('official_accounts c', 'c.id = a.official_account_id')->where(['a.id' => $userIds])->field('a.id, b.name as applet_name, c.name as official_account_name, a.created_at, a.official_account_created_at')->select()->toArray();

            $userResult = array_column($userData, NULL, 'id');
            $type_arr = Money::TYPE_ARR;
            foreach ($data['data'] as $key => $item) {
                $data['data'][$key]['video_name'] = empty($videoResult[$item['video_id']]) ? '' : $videoResult[$item['video_id']];

                $data['data'][$key]['official_account_name'] = empty($userResult[$item['uid']]['official_account_name']) ? '' : $userResult[$item['uid']]['official_account_name'];
                $data['data'][$key]['applet_name'] = empty($userResult[$item['uid']]['applet_name']) ? '' : $userResult[$item['uid']]['applet_name'];
                $data['data'][$key]['applet_created_at'] = empty($userResult[$item['uid']]['created_at']) ? '' : $userResult[$item['uid']]['created_at'];
                $data['data'][$key]['official_account_created_at'] = empty($userResult[$item['uid']]['official_account_created_at']) ? '' : $userResult[$item['uid']]['official_account_created_at'];
                $data['data'][$key]['device_type_text'] = PayRecord::DEVICE_TYPE_ARR[$item['device_type']] ?? 'other';
                $data['data'][$key]['source_text'] = PayRecord::SOURCE_ARR[$item['source']] ?? '';
                $data['data'][$key]['can_refund'] = (!in_array($item['type'],[Money::TYPE_SIGN,Money::TYPE_SHARE]) && $item['status'] == 1 && $item['total_fee'] > 0 && $item['refund_status'] != PayRecord::REFUND_STATUS_SUCC) ? 1 : 0;
                $data['data'][$key]['pay_merchant_name'] = PayRecord::PAY_MERCHANT_ARR[$item['pay_merchant']] ?? '未知';
                $data['data'][$key]['refund_status_text'] = PayRecord::REFUND_STATUS_ARR[$item['refund_status']] ?? '未退款';
                $data['data'][$key]['company_name'] = PayRecord::getCompany($item['id']);
                $data['data'][$key]['type_text'] =$type_arr[$item['type']];
            }
        }

        return $data;
    }

    // 获取充消费记录列表数据
    public function getConsumeList(array $params)
    {
        $page = $params['page'] ?? 1;
        $limit = $params['limit'] ?? 10;

        $where = [];
        if (!empty($params['uid'])) {
            $where[] = ['uid', '=', $params['uid']];
        }

        if (!empty($params['video_id'])) {
            $where[] = ['video_id', '=', $params['video_id']];
        }

        if (!empty($params['start_date'])) {
            $startDate = strtotime($params['start_date'] . ' 00:00:00');
            $where[] = ['created_at', '>=', $startDate];
        }

        if (!empty($params['end_date'])) {
            $endDate = strtotime($params['end_date'] . ' 23:59:59');
            $where[] = ['created_at', '<=', $endDate];
        }

        $data = $this->watchPayRecordModel->getPagingDataByWhere($where, $page, $limit, 'id, uid, link_id, video_id, video_detail_id, money, created_at');

        if (!empty($data['data'])) {

            $videoIds = array_unique(array_column($data['data'], 'video_id'));

            $videoData = $this->videosModel->getDataByIds($videoIds, 'id, name');
            $videoResult = array_column($videoData, 'name', 'id');

            $detailIds = array_unique(array_column($data['data'], 'video_detail_id'));
            $detailData = $this->detailModel->getInfoByIds($detailIds, 'id, sort');

            $detailResult = array_column($detailData, 'sort', 'id');

            foreach ($data['data'] as $key => $item) {
                $data['data'][$key]['video_name'] = empty($videoResult[$item['video_id']]) ? '' : $videoResult[$item['video_id']];
                $data['data'][$key]['sort'] = empty($detailResult[$item['video_detail_id']]) ? '' : $detailResult[$item['video_detail_id']];
            }
        }

        return $data;
    }

    // 获取观看记录列表数据
    public function getWatchList(array $params)
    {
        $page = $params['page'] ?? 1;
        $limit = $params['limit'] ?? 10;

        $where = [];
        if (!empty($params['uid'])) {
            $where[] = ['uid', '=', $params['uid']];
        }

        if (!empty($params['video_id'])) {
            $where[] = ['video_id', '=', $params['video_id']];
        }

        if (!empty($params['start_date'])) {
            $startDate = strtotime($params['start_date'] . ' 00:00:00');
            $where[] = ['created_at', '>=', $startDate];
        }

        if (!empty($params['end_date'])) {
            $endDate = strtotime($params['end_date'] . ' 23:59:59');
            $where[] = ['created_at', '<=', $endDate];
        }

        $data = $this->recordsModel->getPagingDataByWhere($where, $page, $limit, 'id, uid, video_id, video_detail_id, created_at, updated_at');

        if (!empty($data['data'])) {

            $videoIds = array_unique(array_column($data['data'], 'video_id'));

            $videoData = $this->videosModel->getDataByIds($videoIds, 'id, name');
            $videoResult = array_column($videoData, 'name', 'id');

            $detailIds = array_unique(array_column($data['data'], 'video_detail_id'));
            $detailData = $this->detailModel->getInfoByIds($detailIds, 'id, sort');

            $detailResult = array_column($detailData, 'sort', 'id');

            foreach ($data['data'] as $key => $item) {
                if (!empty($item['updated_at'])) {
                    $data['data'][$key]['created_at'] = $item['updated_at'];
                }
                $data['data'][$key]['video_name'] = empty($videoResult[$item['video_id']]) ? '' : $videoResult[$item['video_id']];
                $data['data'][$key]['sort'] = empty($detailResult[$item['video_detail_id']]) ? '' : $detailResult[$item['video_detail_id']];
            }
        }

        return $data;
    }

    // 导出充值
    public function leadingOutRecharge($params)
    {
        ini_set('memory_limit', '-1');
        ini_set('max_execution_time', 0);
        ob_end_clean();
        ob_start();
        $where = [];
        // 用户ID
        if (!empty($params['uid'])) {
            $where[] = ['uid', '=', $params['uid']];
        }

        // 支付状态
        if (!empty($params['status'])) {
            $where[] = ['status', '=', $params['status']];
        }

        // 短剧ID
        if (!empty($params['video_id'])) {
            $where[] = ['video_id', '=', $params['video_id']];
        }

        // 充值类型
        if (!empty($params['type'])) {
            $where[] = ['type', '=', $params['type']];
        }

        // 链接ID
        if (!empty($params['link_id'])) {
            $where[] = ['link_id', '=', $params['link_id']];
        }

        // 投手
        if (!empty($params['responsibility_person_id'])) {
            $linkData = $this->linkModel->getDataByWhere(['responsibility_person_id' => $params['responsibility_person_id']], 'id');
            $linkIds = array_column($linkData, 'id');
            $where[] = ['link_id', 'in', $linkIds];
        }

        // 订单号
        if (!empty($params['order_no'])) {
            $where[] = ['order_no', 'like',  $params['order_no'] . '%'];
        }

        // 时间日期
        if (!empty($params['start_date'])) {
            $startDate = strtotime($params['start_date'] . ' 00:00:00');
            $where[] = ['created_at', '>=', $startDate];
        }

        if (!empty($params['end_date'])) {
            $endDate = strtotime($params['end_date'] . ' 23:59:59');
            $where[] = ['created_at', '<=', $endDate];
        }

        // 来源
        if (!empty($params['source'])) {
            $where[] = ['source', '=', $params['source']];
        }

        // 公司搜索
        if (!empty($params['company_id'])) {
            $responsibilityPerson = $this->responsibilityPersonModel->getDataByWhere(['company_id' => $params['company_id']], 'id, is_distributor');

            $responsibilityPersonIds = array_column($responsibilityPerson, 'id');

            if (!empty($responsibilityPerson[0]['is_distributor'])) {
                $responsibilityPerson = $this->responsibilityPersonModel->getDataByWhere(['pid' => $responsibilityPersonIds], 'id');
                $responsibilityPersonIds = array_column($responsibilityPerson, 'id');
            }

            $linkData1 = $this->linkModel->getDataByWhere(['responsibility_person_id' => $responsibilityPersonIds], 'id');
            $linkIds1 = array_column($linkData1, 'id');
            $where[] = ['link_id', 'in', $linkIds1];
        }

        // 支付商户
        if (!empty($params['pay_merchant'])) {
            $where[] = ['pay_merchant', '=', $params['pay_merchant']];
        }

        // 微信订单号
        if (!empty($params['wechat_order_no'])) {
            $where[] = ['wechat_order_no', 'like', $params['wechat_order_no'] . '%'];
        }

        // 平台订单号
        if (!empty($params['platform_order_no'])) {
            $where[] = ['platform_order_no', 'like', $params['platform_order_no'] . '%'];
        }

        $data = $this->payRecordModel->getInfo($where, 'id, type, uid, order_no, platform_order_no, total_fee, created_at, pay_time, status, link_id, activation_time, video_id, number, pay_merchant,device_type,refund_status','id desc');
        $payMerchantArr = PayRecord::PAY_MERCHANT_ARR;
        $rechargeTypeArr = PayRecord::RECHARGE_TYPE_ARR;
        $refundStatusArr = PayRecord::REFUND_STATUS_ARR;
        $deviceTypeArr = PayRecord::DEVICE_TYPE_ARR;

        if (!empty($data)) {
            $spreadsheet = new Spreadsheet();
            $worksheet = $spreadsheet->getActiveSheet();
            $worksheet->setCellValueByColumnAndRow(1, 1, 'ID');
            $worksheet->setCellValueByColumnAndRow(2, 1, '用户ID');
            $worksheet->setCellValueByColumnAndRow(3, 1, '订单号');
            $worksheet->setCellValueByColumnAndRow(4, 1, '小程序名称');
            $worksheet->setCellValueByColumnAndRow(5, 1, '第三方订单号');
            $worksheet->setCellValueByColumnAndRow(6, 1, '充值类型');
            $worksheet->setCellValueByColumnAndRow(7, 1, '充值金额（元）');
            $worksheet->setCellValueByColumnAndRow(8, 1, 'K币');
            $worksheet->setCellValueByColumnAndRow(9, 1, '下单时间');
            $worksheet->setCellValueByColumnAndRow(10, 1, '支付状态');
            $worksheet->setCellValueByColumnAndRow(11, 1, '支付时间');
            $worksheet->setCellValueByColumnAndRow(12, 1, '支付商户');
            $worksheet->setCellValueByColumnAndRow(13, 1, '链接ID');
            $worksheet->setCellValueByColumnAndRow(14, 1, '激活时间');
            $worksheet->setCellValueByColumnAndRow(15, 1, '公司');
            $worksheet->setCellValueByColumnAndRow(16, 1, '设备类型');
            $worksheet->setCellValueByColumnAndRow(17, 1, '退款状态');
            $linkIds = array_column($data, 'link_id');
            $uids = array_column($data, 'uid');
            $linkIds = array_unique($linkIds);

            $linkData = $this->linkModel->getDataByWhere(['id' => $linkIds], 'id, responsibility_person_id');
            $link = array_column($linkData, 'responsibility_person_id', 'id');

            $responsibilityPerson = $this->responsibilityPersonModel->getDataByWhere([], 'id, company_id');
            $responsibilityPersonData = array_column($responsibilityPerson, 'company_id', 'id');

            $companyIds = array_column($responsibilityPerson, 'company_id');
            $companyData = $this->companyModel->getDataByWhere(['id' => $companyIds], 'id, name');
            $company = array_column($companyData, 'name', 'id');

            $responsibilityPersonAndCompany = [];
            foreach ($responsibilityPersonData as $responsibilityPersonId => $companyId) {
                $responsibilityPersonAndCompany[$responsibilityPersonId] = empty($company[$companyId]) ? 0 : $company[$companyId];
            }

            $linkAndResponsibilityPersonData = [];
            foreach ($link as $linkId => $responsibilityPersonId) {
                $linkAndResponsibilityPersonData[$linkId] = empty($responsibilityPersonAndCompany[$responsibilityPersonId]) ? '' : $responsibilityPersonAndCompany[$responsibilityPersonId];
            }
            $userData = Db::name('user')->alias('a')->leftjoin('applet b', 'b.id = a.applet_id')->whereIn('a.id',$uids)->column('b.name','a.id');
            // 写入内容
            foreach ($data as $key => $item) {
                $typeName = $rechargeTypeArr[$item['type']] ?? '分享';
                $payStatus = $item['status'] == 1 ? '已支付' : '未支付';
                $merchant = $payMerchantArr[$item['pay_merchant']] ?? '未知';

                // 公司名称
                $companyName = empty($linkAndResponsibilityPersonData[$item['link_id']]) ? '未知' : $linkAndResponsibilityPersonData[$item['link_id']];

                $line = $key + 2;
                $worksheet->setCellValueByColumnAndRow(1, $line, $item['id']);
                $worksheet->setCellValueByColumnAndRow(2, $line, $item['uid']);
                // $worksheet->setCellValueByColumnAndRow(3, $line, $item['order_no'], DataType::TYPE_STRING);
                $worksheet->setCellValueExplicitByColumnAndRow(3, $line, $item['order_no'], DataType::TYPE_STRING);
                $worksheet->setCellValueByColumnAndRow(4, $line, $userData[$item['uid']]);
                $worksheet->setCellValueExplicitByColumnAndRow(5, $line, $item['platform_order_no'], DataType::TYPE_STRING);
                $worksheet->setCellValueByColumnAndRow(6, $line, $typeName);
                $worksheet->setCellValueByColumnAndRow(7, $line, $item['total_fee']);
                $worksheet->setCellValueByColumnAndRow(8, $line, $item['number']);
                $worksheet->setCellValueByColumnAndRow(9, $line, date('Y-m-d H:i:s', $item['created_at']));
                $worksheet->setCellValueByColumnAndRow(10, $line, $payStatus);
                $worksheet->setCellValueByColumnAndRow(11, $line, ($item['pay_time'] ? date('Y-m-d H:i:s', $item['pay_time']) : ''));
                $worksheet->setCellValueByColumnAndRow(12, $line, $merchant);
                $worksheet->setCellValueByColumnAndRow(13, $line, $item['link_id']);
                $worksheet->setCellValueByColumnAndRow(14, $line, $item['activation_time']);
                $worksheet->setCellValueByColumnAndRow(15, $line, $companyName);
                $worksheet->setCellValueByColumnAndRow(16, $line, ($deviceTypeArr[$item['device_type']] ?? 'other'));
                $worksheet->setCellValueByColumnAndRow(17, $line, ($refundStatusArr[$item['refund_status']] ?? '未退款'));
            }

            $filename = './充值流水.xlsx';
            if (!empty($params['start_date']) && !empty($params['end_date'])) {
                $filename = $params['start_date'] . '-' . $params['end_date'] . '充值流水.xlsx';
            }

            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
            header('Content-Disposition: attachment;filename="' . $filename . '"');
            header('Cache-Control: max-age=0');
            $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
            $writer->save('php://output');
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
            exit;
        }
    }

    /**
     * 微信支付退款
     *
     * @param id int ly_pay_record表id
     * @return
     */
    public function batchPayRefund($params,$adminUserInfo=null)
    {
        if (empty($params['ids']) || !is_string($params['ids'])) {
            return ['message' => Error::REQUEST_PARAMETER_EXCEPTION];
        }

        $ids = explode(',', $params['ids']);
        $orderData = $this->payRecordModel->getInfo(['id' => $ids], 'id, uid, order_no, wechat_order_no, platform_order_no, pay_merchant, total_fee, status, pay_type, pay_time, link_id, source, official_account_id');

        if (empty($orderData)) {
            return ['message' => Error::ORDER_DATA_NOT_EXISTS];
        }

        $refundReason = '用户投诉'; // 退款原因
        $saveData = [];
        $status = 2;

        // 掌中付支付商户配置
        $mchidInfo = config('enum.wechat_third_party_applet_payment');

        foreach ($orderData as $key => $value) {

            if (empty($value['status'])) {
                return ['message' => Error::ORDER_NOT_PAYMENT];
            }

            if ($value['pay_type'] != 1) {
                return ['message' => Error::ORDER_NOT_WECHAT_PAYMENT];
            }

            // 退款单号
            $refundNo = time() . rand(1000, 10000);

            $refund_client = 1; // 退款客户端发起的标记

            // update by huangxiaoshan 抖音订单退款的话，走抖音的退款通道，2023-07-26
            if ($value['source'] == 2) {
                $userInfo = $this->userModel->getInfoById($value['uid'], 'applet_id'); // 通过下单用户找到其绑定的小程序ID。
                $appletData = $this->appletModel->getInfoById(($userInfo['applet_id'] ?? ''), 'app_id,salt,app_secret'); // 找到支付盐.

                if ($value['pay_merchant'] == PayRecord::PAY_MERCHANT_DY_GENERAL) {

                    $result = $this->dyGeneralRefund($value, $appletData, $refundNo, $refundReason, $value['total_fee']);

                } else {
                    $douyin_refund_url = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_refund';
                    // 构建参数.
                    $requestData = [
                        'app_id' => $appletData['app_id'] ?? '', //小程序APPID
                        'out_order_no' => $value['order_no'], // 商户分配支付单号，标识进行退款的订单
                        'out_refund_no' => $refundNo, // 商户分配退款号，保证在商户中唯一
                        'reason' => $refundReason, // 退款原因
                        'refund_amount' => intval(strval($value['total_fee'] * 100)), // 退款金额，单位分
                        'notify_url' => 'https://' . config('app.app_host') . '/admin/tiktok/refundCallback',
                        'disable_msg' => 0,// 是否屏蔽担保支付的推送消息枚举值
                    ];
                    // 生成签名.
                    $sign = self::douYinsign($requestData, ($appletData['salt'] ?? ''));
                    $requestData['sign'] = $sign;
                    // 发送请求
                    $prepayData = Common::curlRequest($douyin_refund_url, 'POST', json_encode($requestData));
                    // 解析结果
                    $result = json_decode($prepayData, true);
                }
                // 对结果的处理
                if (isset($result['err_no']) && $result['err_no'] == 0) { // 成功
                    $status = 1;
                    $refundPrice = $value['total_fee']; // 退款金额（全退）
                }else{
					$status = 2;
                    $refundPrice = 0;
				}
				$refund_client = 2; // 退款客户端发起的标记
            } else if (in_array($value['pay_merchant'],array_keys(PayRecord::PAY_MERCHANT_MIHUA_ARR))) {
                // 米花支付商户配置
                $enumIndex = PayRecord::PAY_MERCHANT_MIHUA_ARR[$value['pay_merchant']] ?? PayRecord::PAY_MERCHANT_MIHUAAPPLETJUMP_LIUYI;
                $rfMchidInfo = config('enum.'.$enumIndex);
                // 米花第三方退款
                $thirdUrl = 'https://platform.mhxxkj.com/paygateway/mbrefund/orderRefund/v1';

                $requestData = [
                    'merAccount' => $rfMchidInfo['mch_code'],
                    'merchantRefundNo' => $refundNo,
                    'mbOrderId' => $value['platform_order_no'],
                    'time' => time(),
                    'refundAmt' => intval(strval($value['total_fee'] * 100)),
                    'refundCause' => $refundReason,
                    'notifyUrl' => 'https://' . config('app.app_host') . '/api/wx/mhorderrefundcallback',
                ];

                // 获取签名
                $requestData['sign'] = self::getRfSign($requestData, $rfMchidInfo['private_key']);
                // 获取加密串
                $encodeData = self::encryptData($requestData, $rfMchidInfo['private_key']);

                $options = array(
                    'merAccount' => $rfMchidInfo['mch_code'], // 商户标识
                    'data' => $encodeData
                );

                Common::curlPostRequest($thirdUrl, http_build_query($options));
                $status = 0;
            }else if($value['pay_merchant'] == PayRecord::PAY_MERCHANT_VIRTUALPAYMENT){//虚拟支付
                //支付时间超过3天的走之前老的微信官方退款，3天内的走虚拟支付退款
                if ($value['pay_time'] < strtotime('-3 days')){
                    return ['message' => '存在超3天的虚拟支付订单，请登录小程序后台操作退款'];
                }else{
                    $result = $this->virtualRefundOrder($value,$refundNo);
                    // 对结果的处理
                    if (isset($result['errcode']) && $result['errcode'] == 0) { // 成功
                        $status = 1;
                        $refundPrice = $value['total_fee']; // 退款金额（全退）
                    }else{
                        $status = 2;
                        $refundPrice = 0;
                    }
                }
            }else { // 原来的程序.
                if ($value['pay_merchant'] == 5) {
                    // 掌中付第三方退款
                    $thirdUrl = 'https://pay.palmpay.cn/sdkServer/thirdpays/refund';

                    $requestData = [
                        'appid' => $mchidInfo['appid'],
                        'mchId' => $mchidInfo['mch_id'],
                        'pdorderid' => $value['platform_order_no'],
                        'money' => intval(strval($value['total_fee'] * 100)),
                        'version' => '3.0',
                        'outRefundNo' => $refundNo,
                        'refundMoney' => intval(strval($value['total_fee'] * 100)),
                        'refundReason' => $refundReason,
                        'sign' => md5($mchidInfo['mch_id'] . $mchidInfo['appid'] . $value['platform_order_no'] . intval(strval($value['total_fee'] * 100)) . '3.0' . $refundNo . intval(strval($value['total_fee'] * 100)) . $mchidInfo['appkey'])
                    ];

                    $requestUrl = $thirdUrl . '?' . http_build_query($requestData);

                    $result = file_get_contents($requestUrl);

                    $result = json_decode($result, true);

                    if (!empty($result['resultCode'])) {
                        $refundPrice = 0;
                        $status = 2;
                    } else {
                        $refundPrice = round($result['result']['refundMoney'] / 100, 2);
                        $status = 1;
                    }

                } else if ($value['pay_merchant'] == 10) {

                    $rfMchidInfo = config('enum.wechat_applet_mihua_liuyi');
                    // 米花第三方退款
                    $thirdUrl = 'https://platform.mhxxkj.com/paygateway/mbrefund/orderRefund/v1';

                    $requestData = [
                        'merAccount' => $rfMchidInfo['mch_code'],
                        'merchantRefundNo' => $refundNo,
                        'mbOrderId' => $value['platform_order_no'],
                        'time' => time(),
                        'refundAmt' => intval(strval($value['total_fee'] * 100)),
                        'refundCause' => $refundReason
                    ];

                    // 获取签名
                    $requestData['sign'] = self::getRfSign($requestData, $rfMchidInfo['private_key']);
                    // 获取加密串
                    $encodeData = self::encryptData($requestData, $rfMchidInfo['private_key']);

                    $options = array(
                        'merAccount' => $rfMchidInfo['mch_code'], // 商户标识
                        'data' => $encodeData
                    );

                    $result = Common::curlPostRequest($thirdUrl, http_build_query($options));

                    $result = json_decode($result, true);

                    if (!empty($result['code']) && $result['code'] != '000000') {
                        // 退款失败
                        $refundPrice = 0;
                        $status = 2;
                    } else {
                        // 退款成功
                        $refundPrice = round($value['total_fee'], 2);
                        $status = 1;
                    }
                } else {
                    $result = $this->wechatOfficialRefundOrder($value,$value['total_fee'],$refundNo);
                    if (!empty($result['status'])) {
                        if ($result['status'] == 'SUCCESS' || $result['status'] == 'PROCESSING') {
                            $status = 1;
                            $refundPrice = round($result['amount']['refund'] / 100, 2);
                        } else {
                            $status = 2;
                            $refundPrice = 0;
                        }
                    } else {
                        $status = 2;
                        $refundPrice = 0;
                    }
                }
            }
            $now = date('Y-m-d H:i:s');
            $tmpData = [
                'pay_id' => $value['id'],
                'uid' => $value['uid'],
                'refund_no' => $refundNo,
                'order_no' => $value['order_no'],
                'pay_merchant' => $value['pay_merchant'],
                'pay_type' => $value['pay_type'],
                'pay_time' => date('Y-m-d H:i:s', $value['pay_time']),
                'pay_price' => $value['total_fee'],
                'status' => $status,
                'link_id' => $value['link_id'],
                'refund_price' => $refundPrice ?? 0,
                'refund_time' => $status == 1 ? date('Y-m-d H:i:s') : null,
                'refund_client' => $refund_client, // 退款客户端标记 add by huangxiaoshan 2023-07-26
                'operator_id' => $adminUserInfo['user_id'] ?? 0,
                'created_at' => $now,
                'updated_at' => $now,
            ];
            if (in_array($value['pay_merchant'],array_keys(PayRecord::PAY_MERCHANT_MIHUA_ARR))){
                unset($tmpData['refund_time']);
            }
            $saveData[] = $tmpData;
        }

        $this->refundRecordModel->saveAll($saveData);
        return true;
    }

    // 获取加密串
    public function encryptData($params, $privateKey)
    {
        ksort($params);
        $data = self::encrypt(json_encode($params), $privateKey);
        return $data;
    }

    // 加密
    function encrypt($data, $privateKey)
    {
        $makesureProvider = $this->makesureProvider($privateKey, 1);

        $encrypted = '';
        if ($makesureProvider['is_private']) {
            foreach (str_split($data, 117) as $chunk) {
                $r = openssl_private_encrypt($chunk, $encryptData, $makesureProvider['key_provider'], OPENSSL_PKCS1_PADDING);
                $encrypted .= $encryptData;
            }
        } else {
            foreach (str_split($data, 117) as $chunk) {
                $r = openssl_public_encrypt($chunk, $encryptData, $makesureProvider['key_provider'], OPENSSL_PKCS1_PADDING);
                $encrypted .= $encryptData;
            }
        }

        return $r ? $data = base64_encode($encrypted) : null;
    }

    // 
    function makesureProvider($privateKey, $keyFormat)
    {
        $isPrivate = strlen($privateKey) > 500;

        switch ($keyFormat) {
            case 1:
            {

                $privateKey = chunk_split($privateKey, 64, "\r\n");//转换为pem格式的公钥

                if ($isPrivate) {
                    $privateKey = "-----BEGIN PRIVATE KEY-----\r\n" . $privateKey . "-----END PRIVATE KEY-----";
                } else {
                    $privateKey = "-----BEGIN PUBLIC KEY-----\r\n" . $privateKey . "-----END PUBLIC KEY-----";
                }

                break;
            }
        }

        $keyProvider = $isPrivate ? openssl_pkey_get_private($privateKey) : openssl_pkey_get_public($privateKey);
        return ['key_provider' => $keyProvider, 'is_private' => $isPrivate];
    }

    // 获取米花第三方请求签名
    public function getRfSign($requestBody, $keySercet)
    {
        ksort($requestBody);
        $data = '';
        foreach ($requestBody as $key => $value) {
            $data .= trim($value);
        }
        $sign = strtoupper(md5($data . $keySercet));
        return $sign;
    }

    /**
     * @param $nonce_str :32位随机字符串,与上方的随机字符串保持一致
     * @param $signature :上方生成的签名值
     * @return string[]
     */
    public function getHeader($nonceStr, $signature, $time, $randNum)
    {
        $wechatPayConfig = self::getWechatPayConfig($randNum);
        if (!$wechatPayConfig) {
            return [];
        }
        $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%s",signature="%s"', $wechatPayConfig['mch_id'], $wechatPayConfig['serial_no'], $nonceStr, $time, $signature);
        $data = array(
            'Accept: application/json',
            'Content-Type: application/json',
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71',
            "Authorization: WECHATPAY2-SHA256-RSA2048 $token",
        );
        return $data;
    }

    //返回支付商户
    public function getWechatPayConfig($randNum): array
    {
        $wechat_pays =  config('enum.wechat_pay_config');
        foreach ($wechat_pays as $companies) {
            if (isset($companies[$randNum])) {
                return $companies[$randNum];
            }
        }
        return [];
    }

    /**
     * 用于生成签名数据主体
     * @param $disposeUrl :此处的url是接口地址去除域名后的url
     * @param $nonce_str :32位随机字符串
     * @param $requestBody :请求报文的主体
     * @return string
     */
    public function signBody($disposeUrl, $time, $nonce_str, $requestBody)
    {
        $data = array(
            'POST', $disposeUrl, $time, $nonce_str, $requestBody, ''
        );
        return join("\n", $data);
    }

    /**
     * 用于生成签名数据主体
     * @param $disposeUrl :此处的url是接口地址去除域名后的url
     * @param $nonce_str :32位随机字符串
     * @param $requestBody :请求报文的主体
     * @return string
     */
    public function getSignBody($outRefundNo, $nonceStr): string
    {
        $disposeUrl = "/v3/refund/domestic/refunds/$outRefundNo";
        $data = array(
            'GET', $disposeUrl, time(), $nonceStr, '', ''
        );
        return join("\n", $data);
    }

    /**
     * 用于生成签名值
     * @param $data :用于签名的数据
     * @return string
     */
    public function getSignature($data, $randNum, $requestData = [])
    {
        try {
            $private = file_get_contents('./cert/wechat_' . $randNum . '/apiclient_key.pem');
        } catch (\Exception $e) {
            Log::error(json_encode($requestData).' -------- 商户：'.$randNum.' -------- ');
        }

        $key = openssl_pkey_get_private($private);
        openssl_sign($data, $signature, $key, 'sha256WithRSAEncryption');
        return base64_encode($signature);
    }

    // 退款列表
    public function getRefundList($params)
    {
        $page = $params['page'] ?? 1;
        $limit = $params['limit'] ?? 10;

        $where =[];
        if (!empty($params['uid'])) {
            $where[] =['uid', '=', $params['uid']];
        }
        // 日期搜索
        if (!empty($params['start_date']) && !empty($params['end_date'])) {
            $where[] = ['refund_time', 'between', [$params['start_date'], $params['end_date']]];

        }

        if (!empty($params['order_no'])) {
            $where[] = ['order_no', '=', $params['order_no']];
        }

        if (!empty($params['status'])) {
            $where[] = ['status', '=', $params['status']];
        }

        // 公司搜索
        if (!empty($params['company_id'])) {
            $responsibilityPersonData = $this->responsibilityPersonModel->getDataByWhere(['company_id' => $params['company_id']], 'id');
            $linkData = $this->linkModel->getDataByWhere(['responsibility_person_id' => array_column($responsibilityPersonData, 'id')], 'id');
            $where[] = ['link_id', 'in', array_column($linkData, 'id')];
        }

        $data = $this->refundRecordModel->getPagingDataByWhere($where, $page, $limit, 'id, pay_id, uid, refund_no, order_no, pay_merchant, refund_price, pay_type, pay_time, refund_time, pay_price, status, refund_reason');
        if (!empty($data['data'])) {
            foreach ($data['data'] as $key => $item) {
                $data['data'][$key]['company_name'] = PayRecord::getCompany($item['pay_id']);
            }
        }
        return $data;
    }

    // 退款查询
    public function refundQueryDetail($params)
    {
        if (empty($params['id'])) {
            return ['message' => Error::ID_NOT_EXIXTS];
        }

        $data = $this->refundRecordModel->getInfoByWhere(['id' => $params['id']], 'id, pay_id, uid, refund_no, order_no, pay_merchant, refund_price, pay_type, pay_time, refund_time, pay_price, status, refund_client');

        if ($data['refund_client'] == 2) { // 抖音客户端的退款行为
            $userInfo = $this->userModel->getInfoById($data['uid'], 'applet_id'); // 通过下单用户找到其绑定的小程序ID。
            $appletData = $this->appletModel->getInfoById(($userInfo['applet_id'] ?? ''), 'app_id,salt,app_secret'); // 找到支付盐.
            $refund_no = $data['refund_no'];// 退款单号
            // 原因枚举值.
            $refundDetail_array = ['SUCCESS'=>'成功','FAIL'=>'失败','PROCESSING'=>'处理中'];
            if ($data['pay_merchant'] == PayRecord::PAY_MERCHANT_DY_GENERAL) {
                $result = $this->dyGeneralRefundQuery($refund_no, $appletData);
                if (!$result || $result['err_no'] != 0) {
                    $refundInfo = [];
                    $refundDetail = $result['err_msg'] ?? '';
                } else {
                    $refundInfo = $result['data']['refund_list'][0] ?? [];
                    $refundDetail = $refundInfo ? ($refundDetail_array[$refundInfo['refund_status']] ?? '') : '';
                }
                $refundMoney = !empty($refundInfo['refund_total_amount']) ? round($refundInfo['refund_total_amount']/100, 2) : 0;
                $pdRefundNo = $refundInfo['refund_id'] ?? '';
                $refundTime = !empty($refundInfo['refund_at']) ? date('Y-m-d H:i:s', (int)($refundInfo['refund_at']/1000)) : '';

            } else {
                $query_douyin_refund_url = 'https://developer.toutiao.com/api/apps/ecpay/v1/query_refund';
                $requestData = [
                    'app_id' => $appletData['app_id']??'', //小程序APPID
                    'out_refund_no' =>$refund_no , // 商户退款单号
                ];

                // 生成签名.
                $sign = self::douYinsign($requestData, $appletData['salt']??'');
                $requestData['sign'] = $sign;

                // 发送请求
                $prepayData = Common::curlRequest($query_douyin_refund_url, 'POST', json_encode($requestData));
                // 解析结果
                $result = json_decode($prepayData, true);
                $refundMoney = $result['refundInfo']['refund_amount'] ? round(bcdiv($result['refundInfo']['refund_amount'],100,3),2) : 0; // 退款金额

                $refundDetail = $refundDetail_array[$result['refundInfo']['refund_status']] ?? ((isset($result['err_tips']) && $result['err_tips']) ? $result['err_tips'] :'' ); // 退款提示
                // 平台退款单号.
                $pdRefundNo = $result['refundInfo']['refund_no'] ? $result['refundInfo']['refund_no'] : '';
                $refundTime = $result['refundInfo']['refunded_at'] ? date("Y-m-d H:i:s",$result['refundInfo']['refunded_at']) : '';
            }

            $status = (isset($result['err_no']) && $result['err_no'] == 0) ? 1:2;
            $outRefundNo = $data['refund_no']; // 退款单号
            $wxRefundNo = '';
        }   else if (in_array($data['pay_merchant'],array_keys(PayRecord::PAY_MERCHANT_MIHUA_ARR))) {
            // 米花第三方退款查询
            $url = 'https://platform.mhxxkj.com/paygateway/mbrefund/orderRefundQuery/v1';

            $enumIndex = PayRecord::PAY_MERCHANT_MIHUA_ARR[$data['pay_merchant']] ?? PayRecord::PAY_MERCHANT_MIHUAAPPLETJUMP_LIUYI;
            $rfMchidInfo = config('enum.'.$enumIndex);

            $requestData = [
                'merAccount' => $rfMchidInfo['mch_code'],
                'merchantRefundNo' => $data['refund_no'],
                'time' => time()
            ];

            // 获取签名
            $requestData['sign'] = self::getRfSign($requestData, $rfMchidInfo['private_key']);
            // 获取加密串
            $encodeData = self::encryptData($requestData, $rfMchidInfo['private_key']);

            $options = array(
                'merAccount' => $rfMchidInfo['mch_code'], // 商户标识
                'data' => $encodeData
            );

            $result = Common::curlPostRequest($url, http_build_query($options));

            $result = json_decode($result, true);

            if (!empty($result['code']) && $result['code'] != '000000') {
                // 查询失败
                $refundDetail = $result['msg'];
                $status = 2;

                $refundMoney = round($data['refund_price'], 2);
                $refundTime = $data['refund_time'];

                $wxRefundNo = '';
                $outRefundNo = $data['refund_no'];
                $pdRefundNo = '';
            } else {
                // 查询成功
                $refundDetail = '退款成功';
                $status = 1;

                $refundMoney = round($result['data']['refundAmt'], 2);
                $refundTime = $data['refund_time'];

                $wxRefundNo = '';
                $outRefundNo = $data['refund_no'];
                $pdRefundNo = $result['data']['refundNo'];
            }
        } else if($data['pay_merchant'] == PayRecord::PAY_MERCHANT_VIRTUALPAYMENT){//虚拟支付
            $orderData = $this->payRecordModel->getDataByWhere(['id' => $data['pay_id']], 'id, type, uid, order_no, platform_order_no, total_fee, created_at, pay_time, status, link_id, activation_time, video_id, number, pay_merchant,official_account_id');
            $result = $this->queryOrder($orderData);
            if (isset($result['errcode']) && $result['errcode'] == 0 && $result['order']['status'] == 5) { // 成功
                // 查询成功
                $refundDetail = '退款成功';
                $status = 1;

                $refundMoney = round($data['refund_price'], 2);
                $refundTime = $data['refund_time'];

                $wxRefundNo = $result['order']['wx_order_id'];
                $outRefundNo = $data['refund_no'];
                $pdRefundNo = '';
            }else{
                // 查询失败
                $refundDetail = $result['errmsg'] ?? '';
                $status = 2;

                $refundMoney = '';
                $refundTime = '';

                $wxRefundNo = $result['order']['wx_order_id'] ?? '';
                $outRefundNo = '';
                $pdRefundNo = '';
            }
        } else if ($data['pay_merchant'] == PayRecord::PAY_MERCHANT_SAOMAFU) { //扫码富支付
            // 调用第三方请求参数
            $payConfig = config('enum.wechat_third_party_applet_payment4');
            $sign_key = $payConfig['sign_key'] ?? '';
            $body_key = $payConfig['body_key'] ?? '';
            $configs = [
                'outTradeNo' => $data['refund_no'],
            ];
            $saoMaFuService = new SaoMaFuService();
            $options = [
                'nonce' => time(),
                'platformMercCode' => $payConfig['platform_mch_code'] ?? '',
                'reqData' => stripslashes(json_encode($configs, JSON_UNESCAPED_UNICODE)),
            ];
            $options['sign'] = $saoMaFuService->ASCIISHA256($options, $sign_key);
            $options['reqData'] = $saoMaFuService->encryptStr(stripslashes(json_encode($configs, JSON_UNESCAPED_UNICODE)), $body_key);

            $url = $saoMaFuService->domain . '/api/trade/queryRefundOrder.html';
            $queryData = Common::curlRequest($url, 'POST', json_encode($options, JSON_UNESCAPED_UNICODE), [
                "Content-Type: application/json"
            ]);
            flog([
                'message' => '扫码富退款查询数据',
                'returnData' => $queryData,
            ],'sao_ma_query_pay');

            $result = json_decode($queryData, true);

            // 查询失败
            $refundDetail = $result['respMsg'] ?? '';
            $status = 2;
            $refundMoney = '';
            $refundTime = '';
            $wxRefundNo = '';
            $outRefundNo = $data['refund_no']; // 退款单号;
            $pdRefundNo = '';
            if (isset($result['respCode']) && $result['respCode'] == '0000') {
                $respData = $saoMaFuService->decryptStr($result['respData'], $body_key);
                $returnData = json_decode($respData, true);
                flog([
                    'message' => '扫码富退款查询数据2',
                    'returnData' => $returnData,
                ],'sao_ma_query_pay');

                if ($returnData && $returnData['refundStatus'] == 'SUCCESS') {
                    $refundDetail = '退款成功';
                    $status = 1;
                    $refundMoney = $returnData['refundAmount'];
                    $refundTime = $returnData['finishTime'];
                    $pdRefundNo = $returnData['orderNo'];
                }
            }
        }else {
                if ($data['pay_merchant'] == 5) {
                    // 掌中付第三方退款查询
                    $mchidInfo = config('enum.wechat_third_party_applet_payment');
                    $requestData = [
                        'appid' => $mchidInfo['appid'],
                        'mchId' => $mchidInfo['mch_id'],
                        'outRefundNo' => $data['refund_no'],
                        'version' => '3.0',
                        'sign' => md5($mchidInfo['appid'] . $mchidInfo['mch_id'] . '3.0' . $mchidInfo['appkey'])
                    ];

                    $url = 'https://pay.palmpay.cn/sdkServer/thirdpays/refundquery?' . http_build_query($requestData);
                    $result = file_get_contents($url);
                    $result = json_decode($result, true);

                    $keys = array_keys($result['result']);
                    foreach ($keys as $item) {
                        if ($hasStatus = strstr($item, 'status_')) {
                            list($k, $n) = explode('_', $hasStatus);
                        }
                    }

                    if ($result['result']['status_' . $n] == 'success') {
                        $status = 1;
                    } else {
                        $status = 2;
                    }

                    $refundMoney = round($result['result']['refundMoney_' . $n] / 100, 2); // 退款金额
                    $refundDetail = $result['result']['refundDetail_' . $n]; // 退款提示
                    $pdRefundNo = $result['result']['pdRefundNo_' . $n]; // 平台退款单号
                    $outRefundNo = $result['result']['outRefundNo_' . $n]; // 退款单号
                    $refundTime = $data['refund_time'];
                    $wxRefundNo = '';
                } else if ($data['pay_merchant'] == 10) {
                    // 米花第三方退款查询
                    $url = 'https://platform.mhxxkj.com/paygateway/mbrefund/orderRefundQuery/v1';

                    $rfMchidInfo = config('enum.wechat_third_party_applet_payment1');

                    $requestData = [
                        'merAccount' => $rfMchidInfo['mch_code'],
                        'refundNo' => $data['refund_no'],
                        'time' => time()
                    ];

                    // 获取签名
                    $requestData['sign'] = self::getRfSign($requestData, $rfMchidInfo['private_key']);
                    // 获取加密串
                    $encodeData = self::encryptData($requestData, $rfMchidInfo['private_key']);

                    $options = array(
                        'merAccount' => $rfMchidInfo['mch_code'], // 商户标识
                        'data' => $encodeData
                    );

                    $result = Common::curlPostRequest($url, http_build_query($options));

                    $result = json_decode($result, true);

                    if (!empty($result['code']) && $result['code'] != '000000') {
                        // 查询失败
                        $refundDetail = $result['msg'];
                        $status = 2;

                        $refundMoney = round($data['refund_price'], 2);
                        $refundTime = $data['refund_time'];

                        $wxRefundNo = '';
                        $outRefundNo = $data['refund_no'];
                        $pdRefundNo = '';
                    } else {
                        // 查询成功
                        $refundDetail = '退款成功';
                        $status = 1;

                        $refundMoney = round($result['data']['refundAmt'], 2);
                        $refundTime = $data['refund_time'];

                        $wxRefundNo = '';
                        $outRefundNo = $data['refund_no'];
                        $pdRefundNo = $result['data']['refundNo'];
                    }

                } else {
                    // 微信官方退款查询
                    $url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/' . $data['refund_no'];

                    $time = time();
                    $nonceStr = Common::nonceStr(32);

                    // 处理过后的url地址 注:去掉前方的域名即可
                    $disposeUrl = '/v3/refund/domestic/refunds/' . $data['refund_no'];

                    // 获取参与签名的主体数据
                    $signBody = self::getSignBody($data['refund_no'], $nonceStr);
                    // 获取签名值
                    $signature = self::getSignature($signBody, $data['pay_merchant']);
                    $header = self::getHeader($nonceStr, $signature, $time, $data['pay_merchant']);
                    $result = Common::curlGetRequestHeader($url, $header);
                    $result = json_decode($result, true);
                    if (empty($result['status'])) {
                        return ['message' => $result['message']];
                    }
                    switch ($result['status']) {
                        case 'SUCCESS':
                            $status = 1;
                            $refundDetail = '退款成功';
                            break;
                        case 'ABNORMAL':
                            $status = 2;
                            $refundDetail = '退款异常';
                            break;
                        case 'ABNORMAL':
                            $status = 3;
                            $refundDetail = '退款处理中';
                            break;
                        default:
                            $status = 4;
                            $refundDetail = '退款关闭';
                            break;
                    }

                    $refundMoney = round($result['amount']['refund'] / 100, 2);
                    list($date, $time) = explode('T', $result['success_time']);
                    $refundTime = $date . ' ' . substr($time, 0, 8);
                    $wxRefundNo = $result['refund_id'];
                    $outRefundNo = $result['out_refund_no'];
                    $pdRefundNo = '';
                    // [
                    //   "amount" => array:10 [
                    //     "currency" => "CNY"
                    //     "discount_refund" => 0
                    //     "from" => []
                    //     "payer_refund" => 1
                    //     "payer_total" => 1
                    //     "refund" => 1
                    //     "refund_fee" => 0
                    //     "settlement_refund" => 1
                    //     "settlement_total" => 1
                    //     "total" => 1
                    //   ]
                    //   "channel" => "ORIGINAL"
                    //   "create_time" => "2023-04-27T16:39:54+08:00"
                    //   "funds_account" => "AVAILABLE"
                    //   "out_refund_no" => "16825847907703"
                    //   "out_trade_no" => "202304271638353931"
                    //   "promotion_detail" => []
                    //   "refund_id" => "50301305562023042733791444984"
                    //   "status" => "SUCCESS"
                    //   "success_time" => "2023-04-27T16:40:01+08:00"
                    //   "transaction_id" => "4200001746202304275155377083"
                    //   "user_received_account" => "支付用户零钱通"
                    // ]
                }
        }

        return [
            'status' => $status,
            'refund_money' => $refundMoney,
            'refund_detail' => $refundDetail,
            'pd_refund_no' => $pdRefundNo,
            'refund_no' => $outRefundNo,
            'refund_time' => $refundTime,
            'wxRefundNo' => $wxRefundNo
        ];
    }

    /**
     * 抖音签名
     * @param $map
     * @param $salt
     * @return string
     */
    private function douYinsign($map, $salt)
    {
        $rList = array();
        foreach ($map as $k => $v) {
            if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id")
                continue;
            $value = trim(strval($v));
            $len = strlen($value);
            if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"")
                $value = substr($value, 1, $len - 1);
            $value = trim($value);
            if ($value == "" || $value == "null")
                continue;
            array_push($rList, $value);
        }
        array_push($rList, $salt);
        sort($rList, 2);
        return md5(implode('&', $rList));
    }

    // 签名
    public function kwaiSign($body, $appletData)
    {
        $body['app_id'] = $appletData['app_id'];
        ksort($body);
        $string = '';
        foreach ($body as $key => $value) {
            $string .= $key . '=' . $value . '&';
        }
        $string = substr($string, 0, strlen($string) - 1) . $appletData['app_secret'];
        return md5($string);
    }

    /**
     * pay_sig签名算法计算示例
     * @param $uri
     * @param $postBody
     * @param $appKey
     * @return string
     */
    public function calcPaySig($uri, $postBody, $appKey): string
    {
        $needSignMsg = $uri . '&' . $postBody;
        return hash_hmac('sha256', $needSignMsg, $appKey);
    }


    /**
     * 虚拟支付退款接口
     * @return array|int|mixed
     */
    public function virtualRefundOrder($order,$refundNo,$refundFee = 0)
    {
        $uri = '/xpay/refund_order';
        //小程序id改为用户表查询
        $userInfo = $this->userModel->getInfoById($order['uid'], 'link_id, activation_time, official_account_id, interpolate_id, advertiser_id, applet_id, open_id');
        $appletId = $userInfo['applet_id'] ?? 0;
        $appletData = $this->appletModel->getInfoById(['id' => $appletId], 'id, name, url, app_id, app_secret, sandbox_key, prod_key, offer_id');

        // 获取微信accessToken
        $accessToken = Common::getWechatAppletStableAccessToken($appletData['app_id'], $appletData['app_secret'], $appletData['id']);

        if (empty($accessToken)) {
            return ['message' => Error::ACCESS_TOKEN_NOT_EXISTS];
        }
        $fee = $order['total_fee'] * 100;
        // 调用第三方请求参数
        $postBody =  json_encode([
            'openid' => $userInfo['open_id'],
            'order_id' => $order['order_no'],//下单时的单号
            'refund_order_id' => $refundNo,//本次退款时需要传的单号
            'left_fee' => $fee,//当前单剩余可退金额，单位分
            'refund_fee' => $refundFee ? $refundFee * 100 : $fee,//本次退款金额，单位分
            'biz_meta' => '',//商家自定义数据
            'refund_reason' => 1,//退款原因，当前仅支持以下值 0-暂无描述 1-产品问题，影响使用或效果不佳 2-售后问题，无法满足需求 3-意愿问题，用户主动退款 4-价格问题 5:其他原因
            'req_from' => 1,//退款来源，当前仅支持以下值 1-人工客服退款，即用户电话给客服，由客服发起退款流程 2-用户自己发起退款流程 3-其它
            'env' => config('app.env') == 'sandbox' ? 1 : 0,//0-正式环境 1-沙箱环境
        ]);
        // 记录日志
        flog([
            'message' => '微信虚拟支付退款开始',
            'postBody' => $postBody,
        ],'wechat_pay');
        $appKey = config('app.env') == 'sandbox' ? $appletData['sandbox_key'] : $appletData['prod_key'];
        $getUrlLink = config('enum.wechat_pay_base_url').$uri.'?'.http_build_query([
                'access_token' => $accessToken,
                'pay_sig' => $this->calcPaySig($uri, $postBody, $appKey),
            ]);
        $result = Common::curlRequest($getUrlLink, 'POST', $postBody);
        flog([
            'message' => '微信虚拟支付退款结束',
            'result' => $result,
        ],'wechat_pay');
        return json_decode($result, true);
    }

    /**
     * 微信官方退款
     * @param $order
     * @param $refundMoney
     * @param $refundNo
     * @return mixed
     */
    public function wechatOfficialRefundOrder($order, $refundMoney,$refundNo)
    {
        // 微信官方支付退款
        $url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';

        $requestData = [
            'transaction_id' => $order['wechat_order_no'],
            'out_refund_no' => $refundNo,
            'amount' => [
                'refund' => intval(strval($refundMoney * 100)),
                'total' => intval(strval($order['total_fee'] * 100)),
                'currency' => 'CNY'
            ]
        ];
        // 记录日志
        flog([
            'message' => '微信官方支付退款开始',
            'postBody' => $requestData,
        ],'order_refund');

        $time = time();
        $nonceStr = Common::nonceStr(32);

        // 处理过后的url地址 注:去掉前方的域名即可
        $disposeUrl = '/v3/refund/domestic/refunds';
        // 获取请求主体的json数据
        $requestBody = json_encode($requestData);
        // 获取参与签名的主体数据
        $signBody = self::signBody($disposeUrl, $time, $nonceStr, $requestBody);
        // 获取签名值
        $randNum = $order['pay_merchant'] == PayRecord::PAY_MERCHANT_VIRTUALPAYMENT ? 7 : $order['pay_merchant'];
        $signature = self::getSignature($signBody, $randNum, $requestData);

        $header = self::getHeader($nonceStr, $signature, $time, $randNum);

        $result = Common::curlPostRequest($url, json_encode($requestData), $header);
        flog([
            'message' => '微信官方支付退款结束',
            'result' => $result,
        ],'order_refund');
        return json_decode($result, true);
    }


    /**
     * 查询虚拟支付订单
     * @param $order
     * @return array|void
     */
    public function queryOrder($order)
    {
        $userInfo = $this->userModel->getInfoById($order['uid'], 'open_id, applet_id');
        $appletId = $userInfo['applet_id'] ?? 0;
        $appletData = $this->appletModel->getInfoById($appletId, 'id, name, url, app_id, app_secret, sandbox_key, prod_key, offer_id');

        $uri = '/xpay/query_order';
        // 获取微信accessToken
        $accessToken = Common::getWechatAppletStableAccessToken($appletData['app_id'], $appletData['app_secret'], $appletData['id']);

        if (empty($accessToken)) {
            return ['message' => Error::ACCESS_TOKEN_NOT_EXISTS];
        }
        $appKey = config('app.env') == 'sandbox' ? $appletData['sandbox_key'] : $appletData['prod_key'];

        // 调用第三方请求参数
        $postBody =  json_encode([
            'openid' => $userInfo['open_id'],
            'order_id' => $order['order_no'],
            'env' => config('app.env') == 'sandbox' ? 1 : 0,//0-正式环境 1-沙箱环境
        ]);
        $params = [
            'access_token' => $accessToken,
            'pay_sig' => $this->calcPaySig($uri, $postBody,$appKey),
        ];
        $requestUrl = config('enum.wechat_pay_base_url').$uri.'?'.http_build_query($params);
        $result = Common::curlRequest($requestUrl, 'POST', $postBody);
        return json_decode($result, true);
    }

    // 计算退款余额

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws Exception
     */
    public function countRefundMoney($params): float
    {
        $type = $params['refund_type'] ?? RefundRecord::TYPE_ALL;
        $record = PayRecord::where('pay_record.id',$params['id'] ?? 0)
            ->withJoin('user','LEFT')
            ->findOrFail();
        $recordType = $record->type ?? Money::TYPE_COMMON;
        //退款金额
        $refundMoney = $record->total_fee;
        if (in_array($recordType,[Money::TYPE_COMMON,Money::TYPE_ACTIVITY])){
            //部分退款
            //计算公式：部分退款金额 = (总K币余额/当笔K币总数) * 当笔充值金额
            if ($type == RefundRecord::TYPE_PART) {
                if ($record->user->money < $record->number) {
                    $refundMoney = ($record->user->money / $record->number) * $record->total_fee;
                }
            }
        }elseif (in_array($recordType,array_keys(Money::TYPE_VIP_DAYS))){
            //部分退款
            //会员剩余天数
            if ($type == RefundRecord::TYPE_PART) {
                if ($record->user->expiration_time > time()) {
                    $remainDays = $this->getRemainDays($record) - 1;
                    //充值会员类型对应的天数
                    $recordVipDays = Arr::get(Money::TYPE_VIP_DAYS, $recordType);
                    if ($remainDays < $recordVipDays) {
                        $refundMoney = ($remainDays / $recordVipDays) * $record->total_fee;
                    }
                }else{
                    $refundMoney = 0;
                }
            }
        }
        return round($refundMoney,2);
    }

    /**
     * 操作退款
     * @param $params
     * @param $adminUserInfo
     * @return array|bool
     */
    public function doRefund($params, $adminUserInfo = null)
    {
        Db::startTrans();
        try {
            $id = $params['id'] ?? 0;
            $refundReason = $params['refund_reason'] ?? '';
            $type = $params['refund_type'] ?? RefundRecord::TYPE_ALL;
            $refundMoney = $this->countRefundMoney($params);
            if ($refundMoney <= 0){
                throw new Exception('退款金额为0，无需退款');
            }
            $record = PayRecord::where('pay_record.id',$id)
                ->withJoin('user','LEFT')
                ->findOrFail();
            $status = $this->refundOrder($record,$refundMoney,$refundReason,$adminUserInfo);
            //退款成功
            if ($status == RefundRecord::STATUS_SUCC){
                $recordType = $record->type ?? Money::TYPE_COMMON;
                if (in_array($recordType,[Money::TYPE_COMMON,Money::TYPE_ACTIVITY])){
                    $remainMoney = max($record->user->money - $record->number,0);
                    //扣K币
                    $record->user->save([
                        'money' => $remainMoney
                    ]);

                }elseif (in_array($recordType,array_keys(Money::TYPE_VIP_DAYS))){
                    //充值会员类型对应的天数
                    $recordVipDays = Arr::get(Money::TYPE_VIP_DAYS, $recordType ,0);
                    //扣时间
                    if ($type == RefundRecord::TYPE_PART) {
                        if ($record->user->expiration_time > time()) {
                            $remainDays = $this->getRemainDays($record);
                            if ($remainDays <= $recordVipDays) {
                                $record->user->save([
                                    'is_members' => 0,
                                    'expiration_time' => time(),
                                ]);
                            }else{
                                $exTime = $record->user->expiration_time - $recordVipDays * 86400;
                                $data = [
                                    'expiration_time' => max($exTime, time()),
                                ];
                                if ($exTime <= time() || (time() + 86400 > $exTime)){
                                    $data['is_members'] = 0;
                                }
                                $record->user->save($data);
                            }
                        }else{
                            //会员已过期
                            $record->user->save([
                                'is_members' => 0,
                                'expiration_time' => time(),
                            ]);
                        }
                    }else{
                        $exTime = $record->user->expiration_time - $recordVipDays * 86400;
                        $data = [
                            'expiration_time' => max($exTime, time()),
                        ];
                        if ($exTime <= time() || (time() + 86400 > $exTime)){
                            $data['is_members'] = 0;
                        }
                        $record->user->save($data);
                    }
                }
            }
            Db::commit();
            return true;
        }catch (Exception $e){
            Db::rollback();
            return ['message' => $e->getMessage()];
        }
    }

    /**
     * @throws Exception
     */
    public function refundOrder(PayRecord $order, $refundMoney, $refundReason = '', $adminUserInfo = null)
    {
        // 退款单号
        $refundNo = time() . rand(1000, 10000);
        $refund_client = 1; // 退款客户端发起的标记
        // update by huangxiaoshan 抖音订单退款的话，走抖音的退款通道，2023-07-26
        if ($order->source == PayRecord::SOURCE_TIKTOK_APPLET) {

            $userInfo = $this->userModel->getInfoById($order->uid, 'applet_id'); // 通过下单用户找到其绑定的小程序ID。
            $appletData = $this->appletModel->getInfoById(($userInfo['applet_id'] ?? ''), 'app_id,salt,app_secret'); // 找到支付盐.

            if ($order->pay_merchant == PayRecord::PAY_MERCHANT_DY_GENERAL) {   //通用系统支付退款

                $result = $this->dyGeneralRefund($order, $appletData, $refundNo, $refundReason, $refundMoney);

            } else {
                $douyin_refund_url = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_refund';
                // 构建参数.
                $requestData = [
                    'app_id' => $appletData['app_id'] ?? '', //小程序APPID
                    'out_order_no' => $order->order_no, // 商户分配支付单号，标识进行退款的订单
                    'out_refund_no' => $refundNo, // 商户分配退款号，保证在商户中唯一
                    'reason' => $refundReason, // 退款原因
                    'refund_amount' => intval(strval($refundMoney * 100)), // 退款金额，单位分
                    'notify_url' => 'https://' . config('app.app_host') . '/admin/tiktok/refundCallback',
                    'disable_msg' => 0,// 是否屏蔽担保支付的推送消息枚举值
                ];
                flog([
                    'message' => '抖音订单退款请求数据',
                    'requestData' => $requestData,
                ],'order_refund');
                // 生成签名.
                $sign = self::douYinsign($requestData, ($appletData['salt'] ?? ''));
                $requestData['sign'] = $sign;
                // 发送请求
                $prepayData = Common::curlRequest($douyin_refund_url, 'POST', json_encode($requestData));
                // 解析结果
                $result = json_decode($prepayData, true);
                flog([
                    'message' => '抖音订单退款返回结果',
                    'result' => $result,
                ],'order_refund');
            }
            // 对结果的处理
            if (isset($result['err_no']) && $result['err_no'] == 0) { // 成功
                $status = 1;
                $refundPrice = $refundMoney; // 退款金额（全退）
            }else{
                $status = 2;
                $refundPrice = 0;
            }
            $refund_client = 2; // 退款客户端发起的标记
        }else if($order->source == PayRecord::SOURCE_KUAISHOW_APPLET){

            $userInfo = $this->userModel->getInfoById($order->uid, 'applet_id'); // 通过下单用户找到其绑定的小程序ID。
            $appletData = $this->appletModel->getInfoById($userInfo['applet_id'], 'app_id, app_secret');

            // 构建参数.
            $requestData = [
                'out_order_no' => $order->order_no,
                'out_refund_no' => $refundNo,
                'reason' => $refundReason,
                'notify_url' => 'https://' . config('app.app_host') . '/admin/kwai/refundCallback',
                'refund_amount' => intval(strval($refundMoney * 100))
            ];

            flog([
                'message' => '快手订单退款请求数据',
                'requestData' => $requestData,
            ],'order_refund');
            // 生成签名.
            $sign = self::kwaiSign($requestData, $appletData);
            $requestData['sign'] = $sign;
            // 发送请求
            $accessToken = Common::getKwaiAppletAccessToken($appletData['app_id'], $appletData['app_secret']);

            $kwaiRefundUrl = 'https://open.kuaishou.com/openapi/mp/developer/epay/apply_refund?app_id='. $appletData['app_id'] . '&access_token=' . $accessToken;;

            $prepayData = Common::curlRequest($kwaiRefundUrl, 'POST', json_encode($requestData), ['Content-Type: application/json']);

            // 解析结果
            $result = json_decode($prepayData, true);

            flog([
                'message' => '快手订单退款返回结果',
                'result' => $result,
            ],'order_refund');
            if ($result['result'] == 1) {
                $status = 1;
                $refundPrice = $refundMoney; // 退款金额（全退）
            }else{
                $status = 2;
                $refundPrice = 0;
            }
        } else if (in_array($order->pay_merchant,array_keys(PayRecord::PAY_MERCHANT_MIHUA_ARR))) {
            // 米花支付商户配置
            $enumIndex = PayRecord::PAY_MERCHANT_MIHUA_ARR[$order->pay_merchant] ?? PayRecord::PAY_MERCHANT_MIHUAAPPLETJUMP_LIUYI;
            $rfMchidInfo = config('enum.'.$enumIndex);
            // 米花第三方退款
            $thirdUrl = 'https://platform.mhxxkj.com/paygateway/mbrefund/orderRefund/v1';

            $requestData = [
                'merAccount' => $rfMchidInfo['mch_code'],
                'merchantRefundNo' => $refundNo,
                'mbOrderId' => $order->platform_order_no,
                'time' => time(),
                'refundAmt' => intval(strval($refundMoney * 100)),
                'refundCause' => $refundReason,
                'notifyUrl' => 'https://' . config('app.app_host') . '/api/wx/mhorderrefundcallback',
            ];
            flog([
                'message' => '米花订单退款请求数据',
                'requestData' => $requestData,
            ],'order_refund');

            // 获取签名
            $requestData['sign'] = self::getRfSign($requestData, $rfMchidInfo['private_key']);
            // 获取加密串
            $encodeData = self::encryptData($requestData, $rfMchidInfo['private_key']);

            $options = array(
                'merAccount' => $rfMchidInfo['mch_code'], // 商户标识
                'data' => $encodeData
            );

            Common::curlPostRequest($thirdUrl, http_build_query($options));
            $status = 0;
        }else if($order->pay_merchant == PayRecord::PAY_MERCHANT_VIRTUALPAYMENT){//虚拟支付
            //支付时间超过3天的走之前老的微信官方退款，3天内的走虚拟支付退款
            if ($order->pay_time < strtotime('-3 days')){
                throw new Exception('该虚拟支付订单已超3天，请登录小程序后台操作退款或线下联系用户退款');
            }else{
                $result = $this->virtualRefundOrder($order->toArray(),$refundNo,$refundMoney);
                // 对结果的处理
                if (isset($result['errcode']) && $result['errcode'] == 0) { // 成功
                    $status = 1;
                    $refundPrice = $refundMoney; // 退款金额（全退）
                }else{
                    $status = 2;
                    $refundPrice = 0;
                }
            }
        } else if($order->pay_merchant == PayRecord::PAY_MERCHANT_SAOMAFU) {//扫码富支付
            // 退款失败
            $refundPrice = 0;
            $status = 2;
            if ((time() - $order->pay_time) < (180 * 86400)) {
                $saoMaFuService = new SaoMaFuService();
                $configs = [
                    'payOrderNo' => $order->platform_order_no,
                    'payOutTradeNo' => $order->order_no,
                    'outTradeNo' => $refundNo,
                    'refundAmount' => (float)$refundMoney,
                    'notifyUrl' => 'https://' . config('app.app_host') . '/api/wx/saoMaFuRefundCallBack',
                    'refundRemark' => $refundReason ?: '测试数据'
                ];
                // 调用第三方请求参数
                $payConfig = config('enum.wechat_third_party_applet_payment4');
                $sign_key = $payConfig['sign_key'] ?? '';
                $body_key = $payConfig['body_key'] ?? '';
                $options = [
                    'nonce' => time(),
                    'platformMercCode' => $payConfig['platform_mch_code'] ?? '',
                    'reqData' => stripslashes(json_encode($configs, JSON_UNESCAPED_UNICODE)),
                ];
                $options['sign'] = $saoMaFuService->ASCIISHA256($options, $sign_key);
                $options['reqData'] = $saoMaFuService->encryptStr(stripslashes(json_encode($configs, JSON_UNESCAPED_UNICODE)), $body_key);

                $url = $saoMaFuService->domain . '/api/trade/refund.html';
                $data = Common::curlRequest($url, 'POST', json_encode($options, JSON_UNESCAPED_UNICODE), [
                    "Content-Type: application/json"
                ]);
                $result = json_decode($data, true);
                flog([
                    'message' => '扫码富订单退款返回结果',
                    'result' => $result,
                ],'order_refund');

                if (isset($result['respCode']) && $result['respCode'] == '0000') {
                    $respData = $saoMaFuService->decryptStr($result['respData'], $body_key);
                    $returnData = json_decode($respData, true);
                    flog([
                        'message' => '扫码富订单退款返回结果2',
                        'result' => $returnData,
                    ],'order_refund');
                    if ($returnData && isset($returnData['refundStatus']) && $returnData['refundStatus'] == 'REQUEST_SUCCESS') {
                        // 退款成功
                        $refundPrice = round($refundMoney, 2);
                        $status = 1;
                    }
                }
            }
        }else { // 原来的程序.
            if ($order->pay_merchant == PayRecord::PAY_MERCHANT_ZHANGZHONGYUN) {
                // 掌中付支付商户配置
                $mchidInfo = config('enum.wechat_third_party_applet_payment');
                // 掌中付第三方退款
                $thirdUrl = 'https://pay.palmpay.cn/sdkServer/thirdpays/refund';

                $requestData = [
                    'appid' => $mchidInfo['appid'],
                    'mchId' => $mchidInfo['mch_id'],
                    'pdorderid' => $order->platform_order_no,
                    'money' => intval(strval($refundMoney * 100)),
                    'version' => '3.0',
                    'outRefundNo' => $refundNo,
                    'refundMoney' => intval(strval($refundMoney * 100)),
                    'refundReason' => $refundReason,
                    'sign' => md5($mchidInfo['mch_id'] . $mchidInfo['appid'] . $order->platform_order_no . intval(strval($refundMoney * 100)) . '3.0' . $refundNo . intval(strval($refundMoney * 100)) . $mchidInfo['appkey'])
                ];
                flog([
                    'message' => '掌中付订单退款请求数据',
                    'requestData' => $requestData,
                ],'order_refund');

                $requestUrl = $thirdUrl . '?' . http_build_query($requestData);

                $result = file_get_contents($requestUrl);

                $result = json_decode($result, true);
                flog([
                    'message' => '掌中付订单退款返回结果',
                    'result' => $result,
                ],'order_refund');

                if (!empty($result['resultCode'])) {
                    $refundPrice = 0;
                    $status = 2;
                } else {
                    $refundPrice = round($result['result']['refundMoney'] / 100, 2);
                    $status = 1;
                }

            } else if ($order->pay_merchant == PayRecord::PAY_MERCHANT_MIHUALIUYI) {

                $rfMchidInfo = config('enum.wechat_applet_mihua_liuyi');
                // 米花第三方退款
                $thirdUrl = 'https://platform.mhxxkj.com/paygateway/mbrefund/orderRefund/v1';

                $requestData = [
                    'merAccount' => $rfMchidInfo['mch_code'],
                    'merchantRefundNo' => $refundNo,
                    'mbOrderId' => $order->platform_order_no,
                    'time' => time(),
                    'refundAmt' => intval(strval($refundMoney * 100)),
                    'refundCause' => $refundReason,
                    'notifyUrl' => 'https://' . config('app.app_host') . '/api/wx/mhorderrefundcallback',
                ];
                flog([
                    'message' => '米花比翼鸟主体订单退款请求数据',
                    'requestData' => $requestData,
                ],'order_refund');
                // 获取签名
                $requestData['sign'] = self::getRfSign($requestData, $rfMchidInfo['private_key']);
                // 获取加密串
                $encodeData = self::encryptData($requestData, $rfMchidInfo['private_key']);

                $options = array(
                    'merAccount' => $rfMchidInfo['mch_code'], // 商户标识
                    'data' => $encodeData
                );

                $result = Common::curlPostRequest($thirdUrl, http_build_query($options));

                $result = json_decode($result, true);
                flog([
                    'message' => '米花比翼鸟主体订单退款返回结果',
                    'result' => $result,
                ],'order_refund');

                if (!empty($result['code']) && $result['code'] != '000000') {
                    // 退款失败
                    $refundPrice = 0;
                    $status = 2;
                } else {
                    // 退款成功
                    $refundPrice = round($refundMoney, 2);
                    $status = 1;
                }
            } else {
                $result = $this->wechatOfficialRefundOrder($order->toArray(),$refundMoney,$refundNo);
                if (!empty($result['status'])) {
                    if ($result['status'] == 'SUCCESS' || $result['status'] == 'PROCESSING') {
                        $status = 1;
                        $refundPrice = round($result['amount']['refund'] / 100, 2);
                    } else {
                        $status = 2;
                        $refundPrice = 0;
                    }
                } else {
                    $status = 2;
                    $refundPrice = 0;
                }
            }
        }
        $now = date('Y-m-d H:i:s');
        $saveData = [
            'pay_id' => $order->id,
            'uid' => $order->uid,
            'refund_no' => $refundNo,
            'order_no' => $order->order_no,
            'pay_merchant' => $order->pay_merchant,
            'pay_type' => $order->pay_type,
            'pay_time' => date('Y-m-d H:i:s', $order->pay_time),
            'pay_price' => $order->total_fee,
            'status' => $status,
            'link_id' => $order->link_id,
            'refund_price' => $refundPrice ?? 0,
            'refund_time' => $status == 1 ? date('Y-m-d H:i:s') : null,
            'refund_client' => $refund_client, // 退款客户端标记 add by huangxiaoshan 2023-07-26
            'operator_id' => $adminUserInfo['user_id'] ?? 0,
            'created_at' => $now,
            'updated_at' => $now,
            'refund_reason' => $refundReason,
        ];
        if (in_array($order->pay_merchant,array_keys(PayRecord::PAY_MERCHANT_MIHUA_ARR))){
            unset($saveData['refund_time']);
        }
        $order->save(['refund_status' => $status]);
        $this->refundRecordModel->save($saveData);
        return $status;
    }

    // 订单明细
    public function orderDetail($params)
    {
        $record = PayRecord::where('pay_record.id',$params['id'])
            ->withJoin([
                'user'   =>  ['id', 'expiration_time', 'money'],
                'video'   =>  ['id', 'name','update_sort']
            ], 'left')
            ->findOrFail();
        $record['type_text'] = Money::TYPE_ARR[$record->type] ?? '';
        $record['expiration_time_text'] = !empty($record->user->expiration_time) ? date('Y-m-d H:i:s',$record->user->expiration_time) : '已清空';
        $remainDays = $this->getRemainDays($record);
        $record['remain_days'] = $remainDays > 0 ? $remainDays : '已过期';
        return $record;
    }

    /**
     * 获取会员剩余天数
     * @param $record
     * @return int
     */
    public function getRemainDays($record): int
    {
        $remainDays = 0;
        if (isset($record->user->expiration_time) && $record->user->expiration_time > time()){
            $remainDays = (new Carbon())->diffInDays(Carbon::parse($record->user->expiration_time));
        }
        return $remainDays;
    }

    // 退款记录导出
    public function refundLeadingOutRecharge($params)
    {
        ini_set('memory_limit', '-1');
        ini_set('max_execution_time', 0);
        ob_end_clean();
        ob_start();

        $where =[];
        if (!empty($params['uid'])) {
            $where[] =['uid', '=', $params['uid']];
        }
        // 日期搜索
        if (!empty($params['start_date']) && !empty($params['end_date'])) {
            $where[] = ['refund_time', 'between', [$params['start_date'], $params['end_date']]];

        }

        if (!empty($params['order_no'])) {
            $where[] = ['order_no', '=', $params['order_no']];
        }

        if (!empty($params['status'])) {
            $where[] = ['status', '=', $params['status']];
        }

        // 公司搜索
        if (!empty($params['company_id'])) {
            $responsibilityPersonData = $this->responsibilityPersonModel->getDataByWhere(['company_id' => $params['company_id']], 'id');
            $linkData = $this->linkModel->getDataByWhere(['responsibility_person_id' => array_column($responsibilityPersonData, 'id')], 'id');
            $where[] = ['link_id', 'in', array_column($linkData, 'id')];
        }
        $field = 'id, pay_id, uid, refund_no, order_no, pay_merchant, refund_price, pay_type, pay_time, refund_time, pay_price, status, refund_reason';
        $data = Db::name('refund_record')->where($where)->field($field)->order('id','desc')->select()->toArray();
        if (!empty($data)) {
            foreach ($data as $key => $item) {
                $data[$key]['company_name'] = PayRecord::getCompany($item['pay_id']);
            }
        }

        if (!empty($data)) {
            $spreadsheet = new Spreadsheet();
            $worksheet = $spreadsheet->getActiveSheet();
            $worksheet->setCellValueByColumnAndRow(1, 1, '用户ID');
            $worksheet->setCellValueByColumnAndRow(2, 1, '支付ID');
            $worksheet->setCellValueByColumnAndRow(3, 1, '订单号');
            $worksheet->setCellValueByColumnAndRow(4, 1, '退款单号');
            $worksheet->setCellValueByColumnAndRow(5, 1, '支付金额');
            $worksheet->setCellValueByColumnAndRow(6, 1, '退款金额');
            $worksheet->setCellValueByColumnAndRow(7, 1, '公司名称');
            $worksheet->setCellValueByColumnAndRow(8, 1, '商户');
            $worksheet->setCellValueByColumnAndRow(9, 1, '状态');
            $worksheet->setCellValueByColumnAndRow(10, 1, '支付时间');
            $worksheet->setCellValueByColumnAndRow(11, 1, '退款时间');
            $worksheet->setCellValueByColumnAndRow(12, 1, '退款原因');

           

            // 写入内容
            $line = 0;
            foreach ($data as $key => $item) {

                

                if ($item['status'] == 1) {
                    $payStatus = '成功';
                } else {
                    $payStatus = '失败';
                }

                // 1比翼鸟剧场 2比翼鸟文化 3橙子剧场 4比翼鸟传媒 5第三方
                switch ($item['pay_merchant']) {
                    case 1:
                        $merchant = '比翼鸟剧场';
                        break;
                    case 2:
                        $merchant = '比翼鸟文化';
                        break;
                    case 3:
                        $merchant = '橙子剧场';
                        break;
                    case 4:
                        $merchant = '比翼鸟传媒';
                        break;
                    case 5:
                        $merchant = '掌中付第三方';
                        break;
                    case 6:
                        $merchant = '比翼鸟短剧';
                        break;
                    case 7:
                        $merchant = '比翼鸟';
                        break;
                    case 8:
                        $merchant = '比翼鸟看好剧';
                        break;
                    case 9:
                        $merchant = '比翼鸟小剧场';
                        break;
                    case 10:
                        $merchant = '米花第三方（比翼鸟公司主体）';
                        break;
                    case 13:
                        $merchant = '米花第三方小程序跳转（比翼鸟主体）';
                        break;
                    case 14:
                        $merchant = '米花第三方小程序跳转（大连趣漫主体）';
                        break;
                    case 15:
                        $merchant = '小程序虚拟支付';
                        break;
                    default:
                        $merchant = '未知';
                        break;
                }

                $line = $key + 2;
                $worksheet->setCellValueByColumnAndRow(1, $line, $item['uid']);
                $worksheet->setCellValueExplicitByColumnAndRow(2, $line, $item['pay_id'], DataType::TYPE_STRING);
                $worksheet->setCellValueExplicitByColumnAndRow(3, $line, $item['order_no'], DataType::TYPE_STRING);
                $worksheet->setCellValueExplicitByColumnAndRow(4, $line, $item['refund_no'], DataType::TYPE_STRING);
                $worksheet->setCellValueByColumnAndRow(5, $line, $item['pay_price']);
                $worksheet->setCellValueByColumnAndRow(6, $line, $item['refund_price']);
                $worksheet->setCellValueByColumnAndRow(7, $line, $item['company_name']);
                $worksheet->setCellValueByColumnAndRow(8, $line, $merchant);
                $worksheet->setCellValueByColumnAndRow(9, $line, $payStatus);
                $worksheet->setCellValueByColumnAndRow(10, $line, $item['pay_time']);
                $worksheet->setCellValueByColumnAndRow(11, $line, $item['refund_time']);
                $worksheet->setCellValueByColumnAndRow(12, $line, $item['refund_reason']);
            }

            $filename = './退款导出.xlsx';
            if (!empty($params['start_date']) && !empty($params['end_date'])) {
                $filename = $params['start_date'] . '-' . $params['end_date'] . '退款导出.xlsx';
            }

            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
            header('Content-Disposition: attachment;filename="' . $filename . '"');
            header('Cache-Control: max-age=0');
            $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
            $writer->save('php://output');
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
            exit;
        }
    }

    public function dyGeneralRefund($record, $applet, $refundNo, $refundReason, $refundAmount)
    {
        $accessToken = Common::getTikTokDeveloperClientToken($applet['app_id'], $applet['app_secret']);
        $header = [
            'Content-Type: application/json',
            'access-token: ' . $accessToken
        ];
        $queryData = Common::curlRequest(
            'https://open.douyin.com/api/trade_basic/v1/developer/order_query',
            'POST',
            json_encode(['order_id' => $record['platform_order_no']], JSON_UNESCAPED_UNICODE),
            $header
        );
        flog(['result' => $queryData, 'order_no' => $refundNo], 'dy_refund_result1');
        $query = json_decode($queryData, true);
        if(!$query || $query['err_no'] != 0) return ['err_no' => 1002];

        $item_detail = !empty($query['data']['item_order_list']) ? $query['data']['item_order_list'][0] : [];
        if (!$item_detail) return ['err_no' => 1003];

        $url = 'https://open.douyin.com/api/trade_basic/v1/developer/refund_create/';
        $host = config('app.app_host');
        if (strpos($host, 'test') !== 0) {
            $host = 'ht.' . $host;
        }
        $body = [
            "order_id" => $record['platform_order_no'],
            "out_refund_no" => $refundNo,
            "order_entry_schema" => [
                "path" => 'pages/rechargerecord/rechargerecord',
                "params" => '',
            ],
            "refund_total_amount" => intval($refundAmount * 100),
            "notify_url" => 'https://' . $host . '/admin/tiktok/newrefundcallback',
            "item_order_detail" => [
                    [
                        "item_order_id" => $item_detail['item_order_id'] ?? '',
                        "refund_amount" => intval($refundAmount * 100),
                    ]
            ],
            "refund_reason" => [
                [
                    "code" => 101,
                    "text" => $refundReason
                ]
            ],
        ];

        // 发送请求
        $prepayData = Common::curlRequest($url, 'POST', json_encode($body, JSON_UNESCAPED_UNICODE), $header);
        flog(['result' => $prepayData, 'order_no' => $refundNo], 'dy_refund_result');
        // 解析结果
        $result = json_decode($prepayData, true);
        return [
            'err_no' => (!$result || $result['err_no'] != 0) ? 1001 : 0,
        ];
    }

    public function dyGeneralRefundQuery($refund_no, $applet)
    {
        $url = 'https://open.douyin.com/api/trade_basic/v1/developer/refund_query/';
        $body = [
            'out_refund_no' => $refund_no,
        ];

        $accessToken =  Common::getTikTokDeveloperClientToken($applet['app_id'], $applet['app_secret']);
        $header = [
            'Content-Type: application/json',
            'access-token: ' . $accessToken
        ];

        // 发送请求
        $prepayData = Common::curlRequest($url, 'POST', json_encode($body, JSON_UNESCAPED_UNICODE), $header);
        flog(['result' => $prepayData], 'dy_refund_detail');
        // 解析结果
        return json_decode($prepayData, true);
    }

}